/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.core.be;

import com.inspur.edp.bef.core.buffer.BEBufferManager;
import com.inspur.edp.bef.core.buffer.TransBeBufferChangeManager;
import com.inspur.edp.bef.core.changeset.BEChangesetManager;
import com.inspur.edp.bef.core.session.BEFuncSessionBase;
import com.inspur.edp.bef.core.session.FuncSession;
import com.inspur.edp.cef.entity.changeset.AddChangeDetail;
import com.inspur.edp.cef.entity.changeset.ChangeDetailMerger;
import com.inspur.edp.commonmodel.core.session.distributed.RootEditToken;
import com.inspur.edp.bef.core.session.transaction.IBefTransactionItem;
import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.api.dataType.base.IAccessorCreator;
import com.inspur.edp.cef.api.session.ICefSessionItem;
import com.inspur.edp.cef.entity.accessor.base.IAccessor;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.commonmodel.core.session.distributed.dac.FuncSessionItemIncrement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

//失败后回滚模式
public class BeBufferChangeManager implements IBeBufferChangeManager {

  private FuncSession session;
  private String entityType;
  private BEBufferManager beBufferMgr;
  private BEChangesetManager beChangesetMgr;
  private RootEditToken token;

  public BeBufferChangeManager(FuncSession session, String entityType,
      IAccessorCreator accCreator) {
    this.session = session;
    this.entityType = entityType;

    beBufferMgr = new BEBufferManager(session, entityType, accCreator);
    beChangesetMgr = new BEChangesetManager(entityType, session);
  }

  //region BEBufferManager

  //region InitBuffer

  public IAccessor createCurrentBuffer(IEntityData data) {
    return beBufferMgr.createCurrentBuffer(data);
  }


  public IEntityData initTransactionBuffer(String id) {
    return beBufferMgr.initTransactionBuffer(id);
  }

  public IEntityData initCurrentBuffer(String id) {
    return beBufferMgr.initCurrentBuffer(id);
  }

  //endregion

  //region GetData

  public IEntityData getCurrentData(String dataId) {
    return beBufferMgr.getCurrentData(dataId);
  }


  public IEntityData getTransactionData(String dataId) {
    return beBufferMgr.getTransactionData(dataId);
  }


  public IEntityData getOriginalData(String dataId) {
    return beBufferMgr.getOriginalData(dataId);
  }

  //endregion

  //region Accept/Reject

  public IAccessor acceptTemplateCurrentChange(String id, IChangeDetail change) {
    return beBufferMgr.acceptTemplateCurrentChange(id, change);
  }

  public IAccessor acceptCurrent(String id) {
    IChangeDetail currChange = getCurrentChange(id);
    if(currChange == null){
      return (IAccessor) getCurrentData(id);
    }
    increase(currChange);
    IAccessor result = beBufferMgr.acceptCurrent(id, currChange);
    beChangesetMgr.acceptCurrentChange(id);
    return result;
  }

  public IAccessor rejectCurrent(String id) {
    IAccessor result = beBufferMgr.rejectCurrent(id, getCurrentChange(id));
    beChangesetMgr.rejectCurrentChange(id);
    return result;
  }

  public IAccessor acceptTransaction(String id) {
    IAccessor result = beBufferMgr.acceptTransaction(id, getTransactionChange(id));
    beChangesetMgr.acceptTransactionChange(id);
    return result;
  }

  public void rejectTransaction(String id, RefObject<IEntityData> current,
      RefObject<IEntityData> transaction) {
    rejectCurrentTemplateChange(id);
    IChangeDetail tranChange = getTransactionChange(id);
    if (tranChange == null) {
      current.argvalue = getCurrentData(id);
      transaction.argvalue = getTransactionData(id);
      return;
    }
    transaction.argvalue = (IEntityData) beBufferMgr.rejectTransaction(id, tranChange);
    current.argvalue = (IEntityData) beBufferMgr.rejectCurrent(id, tranChange);
    beChangesetMgr.rejectCurrentChange(id);
    beChangesetMgr.rejectTransaction(id);
  }

  //endregion

  //endregion

  //region BEChangeManager

  public IChangeDetail getCurrentTemplateChange(String id) {
    return beChangesetMgr.getCurrentTemplateChange(id);
  }

  public IChangeDetail getCurrentChange(String id) {
    return beChangesetMgr.getCurrentChange(id);
  }

  public IChangeDetail getTransactionChange(String id) {
    return beChangesetMgr.getTransactionChange(id);
  }


  public void acceptCurrentTemplateChange(String id) {
    beChangesetMgr.acceptCurrentTemplateChange(id);
  }

  //public void acceptCurrentChange(string id) { return  beChangesetMgr.acceptCurrentChange(id);


  public void acceptTransactionChange(String id) {
    beChangesetMgr.acceptTransactionChange(id);
  }


  public void appendCurrentTemplateChange(IChangeDetail change) {
    beChangesetMgr.appendCurrentTemplateChange(change);
  }


  public void rejectCurrentTemplateChange(String id) {
    beChangesetMgr.rejectCurrentTemplateChange(id);
  }


  public void rejectCurrentChange(String id) {
    beChangesetMgr.rejectCurrentChange(id);
  }

  //public void rejectTransaction(string id) { return  beChangesetMgr.rejectTransaction(id);


  public void initCurrentTemplateChange(IChangeDetail change) {
    beChangesetMgr.initCurrentTemplateChange(change);
  }


  public void initListenerChange(IChangeDetail change) {
    beChangesetMgr.initListenerChange(change);
  }


  public IChangeDetail getListenerChange(String id) {
    return beChangesetMgr.getListenerChange(id);
  }


  public void acceptListenerChange(String ID) {
    beChangesetMgr.acceptListenerChange(ID);
  }

  //endregion

  //region IBufferManager impl

  public IAccessor accept(String dataId, int level, IChangeDetail change) {
    return beBufferMgr.accept(dataId, level, change);
  }


  public IAccessor getBuffer(String dataId, int level) {
    return beBufferMgr.getBuffer(dataId, level);
  }


  public IAccessor getBuffer(String dataId) {
    return beBufferMgr.getBuffer(dataId);
  }


  public IAccessor initBufferByBaseLevel(String id, int level, boolean isReadonly) {
    return beBufferMgr.initBufferByBaseLevel(id, level, isReadonly);
  }


  public IAccessor initBufferByLevel(IEntityData data, int level, boolean isReadonly) {
    return beBufferMgr.initBufferByLevel(data, level, isReadonly);
  }


  public IAccessor initBuffer_ZeroLevel(IEntityData data, boolean isReadonly) {
    return beBufferMgr.initBuffer_ZeroLevel(data, isReadonly);
  }


  public IAccessor reject(String dataId, int level, IChangeDetail change) {
    return beBufferMgr.reject(dataId, level, change);
  }


  public void rejectBufferZeroLevel(String id) {
    beBufferMgr.rejectBufferZeroLevel(id);
  }

  @Override
  public void remove(String s) {
    beBufferMgr.remove(s);
    beChangesetMgr.remove(s);
  }

  //endregion

  //region IChangesetManager impl

  public void acceptChange(String id, int level) {
    beChangesetMgr.acceptChange(id, level);
  }

  public IChangeDetail getChange(String id, int level) {
    return beChangesetMgr.getChange(id, level);
  }

  @Override
  public void clearChange() {
    beChangesetMgr.clear();
  }

//  @Override
//  public boolean isEmpty(String id) {
//    return !beBufferMgr.hasData(id) && !beChangesetMgr.anyChange(id);
//  }
  //endregion

  //region Transaction

  public IBefTransactionItem begin() {
    return new TransBeBufferChangeManager(this);
  }

  public void commit(IBefTransactionItem upper) {
  }

  public void rollBack() {
  }

  public final void innerRollBack(Map<String, IChangeDetail> undoLog, Map<String, IChangeDetail> changeBackup) {
    ICefSessionItem sessionItem = session.getSessionItems().get(entityType);
    BEFuncSessionBase besessionitem = (BEFuncSessionBase) sessionItem;

    for (Map.Entry<String, IChangeDetail> changePair : undoLog.entrySet()) {
      IChangeDetail undoChange = changePair.getValue();
      IEntityData tData,cData;
      if(undoChange instanceof AddChangeDetail) {
        tData = ((AddChangeDetail)undoChange).getEntityData();
        cData = beBufferMgr.atomicalRollback4Add(tData);
      } else {
        cData = (IEntityData) acceptTemplateCurrentChange(changePair.getKey(), undoChange);
        tData = (IEntityData) beBufferMgr.acceptCurrent(changePair.getKey(), undoChange);
      }
      //var cData = InitCurrentBuffer(changePair.Key);
      if (besessionitem != null) {
        BusinessEntity beEntity = (BusinessEntity) besessionitem.getBizEntityCacheManager()
            .getEntity(changePair.getKey());
        if (beEntity.getBEContext().getCurrentData() != null) {
          beEntity.getBEContext().stopChangeListener();
        }
        beEntity.getBEContext().setCurrentData(cData);
        beEntity.getBEContext().setTransactionData(tData);
        if (cData != null) {
          beEntity.getBEContext().restartChangeListener();
        }
      }
    }

    for (Map.Entry<String, IChangeDetail> changePair : changeBackup.entrySet()) {
      beChangesetMgr.rollBack(changePair.getKey(), changePair.getValue());
    }
    unincrease(undoLog);
  }

  //endregion

  //region increment
  private Map<String, IChangeDetail> incChanges;
  @Override
  public void endEdit(RootEditToken token) {
    this.token = null;
    if(incChanges == null || incChanges.isEmpty()) {
      return;
    }
    FuncSessionItemIncrement inc = token.getItemIncrement(entityType);
    inc.setChangeDetails(new ArrayList<>(incChanges.values()));
    this.incChanges = null;
  }

  @Override
  public void beginEdit(RootEditToken token) {
    this.token = token;
  }

  private void increase(IChangeDetail change) {
    if(token == null) {
      return;
    }
    if(incChanges == null) {
      incChanges = new HashMap<>();
    }
    IChangeDetail existing = incChanges.get(change.getDataID());
    if(existing == null) {
      incChanges.put(change.getDataID(), change);
    } else {
      IChangeDetail tranChange = getTransactionChange(change.getDataID());
      if(tranChange == null || tranChange != existing) {
        incChanges.put(change.getDataID(), ChangeDetailMerger.mergeChangeDetail(change, existing));
      }
    }
  }

  private void unincrease(Map<String, IChangeDetail> undo) {
    if(token == null || undo == null || undo.isEmpty()) {
      return;
    }
    if(incChanges != null) {
      for (String id : undo.keySet()) {
        incChanges.remove(id);
      }
    }
  }

  @Override
  public void notifySave() {
    notifyReset();
  }

  public void notifyReset() {
    if(token == null) {
      return;
    }
    FuncSessionItemIncrement inc = token.getItemIncrement(entityType);
    inc.setReset(true);
    if(incChanges != null) {
      incChanges.clear();
      incChanges = null;
    }
  }

//  @Override
//  public void recover(FuncSessionItemIncrement increment) {
//    if(increment.isChangeReset()){
////      increment.
//    }
//    if(increment.getChangeDetails() != null) {
//      for (IChangeDetail change : increment.getChangeDetails()) {
//        if (change instanceof AddChangeDetail) {
//          IAccessor acc = createCurrentBuffer(((AddChangeDetail) change).getEntityData());
//          change = new AddChangeDetail((IEntityData) acc);
//        }
//        appendCurrentTemplateChange(change);
//        acceptCurrentTemplateChange(change.getDataID());
//        acceptCurrentCore(change.getDataID());
//      }
//    }
//  }
  //endregion
}
